{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 场景一：角色扮演的写作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pulling manifest\n",
      "pulling 8359bebea988... 100% |██████████████████| (7.4/7.4 GB, 16 TB/s)        \n",
      "pulling 65c6ec5c6ff0... 100% |████████████████████| (45/45 B, 1.8 MB/s)        \n",
      "pulling dd36891f03a0... 100% |████████████████████| (31/31 B, 1.3 MB/s)        \n",
      "pulling f94f529485e6... 100% |███████████████████| (382/382 B, 17 MB/s)        \n",
      "verifying sha256 digest\n",
      "writing manifest\n",
      "removing any unused layers\n",
      "success\n"
     ]
    }
   ],
   "source": [
    "!ollama pull llama2-chinese:13b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install langchain langchain-core langchain-community"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'👋祝大家好！我今天特地为大家推荐了一本新书《LangChain实战》，让我们一起开始来学习 LangChain 吧！📚💻这本书是由一些专业的技术人员编写的，内容十分透彻、实用。🤝如果大家想要提高自己的编程水平，或者了解更多的开发框架，这本书一定会对你有所帮助。👍赞！\\n'"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_community.chat_models import ChatOllama\n",
    "\n",
    "# 设定系统上下文，构建提示词\n",
    "template = \"\"\"请扮演一位资深的技术博主，您将负责为用户生成适合在微博发送的中文帖文。\n",
    "请把用户输入的内容扩展成 140 字左右的文字，并加上适当的 emoji 使内容引人入胜并专业。\"\"\"\n",
    "prompt = ChatPromptTemplate.from_messages([(\"system\", template), (\"human\", \"{input}\")])\n",
    "\n",
    "# 通过 Ollama 加载 Llama 2 13B 对话补全模型\n",
    "model = ChatOllama(model=\"llama2-chinese:13b\")\n",
    "\n",
    "# 通过 LCEL 构建调用链并执行得到文本输出\n",
    "chain = prompt | model | StrOutputParser()\n",
    "chain.invoke({ \"input\": \"给大家推荐一本新书《LangChain实战》，让我们一起开始来学习 LangChain 吧！\"})\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='\\n小白兔在花草间跃步而来。看到了一颗美味的草根，便用长长的长着想吃下去。却发现这只不过是一个似人像的玩物。小白兔开始笑起来，感到十分惊喜。他懂得如何享受生活中的美好事物。\\n')"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_community.chat_models import ChatOllama\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"请编写一篇关于{topic}的中文小故事，不超过100字\")\n",
    "model = ChatOllama(model=\"llama2-chinese:13b\")\n",
    "\n",
    "chain = prompt | model\n",
    "chain.invoke({\"topic\": \"小白兔\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Tell me a funny joke about rabbit.'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "prompt_template = PromptTemplate.from_template(\n",
    "    \"Tell me a {adjective} joke about {content}.\"\n",
    ")\n",
    "prompt_template.format(adjective=\"funny\", content=\"rabbit\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[SystemMessage(content='You are a helpful AI bot. Your name is Bob.'),\n",
       " HumanMessage(content='Hello, how are you doing?'),\n",
       " AIMessage(content=\"I'm doing well, thanks!\"),\n",
       " HumanMessage(content='What is your name?')]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "\n",
    "chat_template = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"You are a helpful AI bot. Your name is {name}.\"),\n",
    "        (\"human\", \"Hello, how are you doing?\"),\n",
    "        (\"ai\", \"I'm doing well, thanks!\"),\n",
    "        (\"human\", \"{user_input}\"),\n",
    "    ]\n",
    ")\n",
    "chat_template.format_messages(name=\"Bob\", user_input=\"What is your name?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Give the antonym of every input\n",
      "\n",
      "Input: happy\n",
      "Output: sad\n",
      "\n",
      "Input: tall\n",
      "Output: short\n",
      "\n",
      "Input: energetic\n",
      "Output: lethargic\n",
      "\n",
      "Input: sunny\n",
      "Output: gloomy\n",
      "\n",
      "Input: windy\n",
      "Output: calm\n",
      "\n",
      "Input: big\n",
      "Output:\n",
      "\n",
      "\n",
      "Give the antonym of every input\n",
      "\n",
      "Input: happy\n",
      "Output: sad\n",
      "\n",
      "Input: big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else\n",
      "Output:\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_core.prompts import FewShotPromptTemplate\n",
    "from langchain.prompts.example_selector import LengthBasedExampleSelector\n",
    "\n",
    "# 创建一些反义词输入输出的示例内容\n",
    "examples = [\n",
    "    {\"input\": \"happy\", \"output\": \"sad\"},\n",
    "    {\"input\": \"tall\", \"output\": \"short\"},\n",
    "    {\"input\": \"energetic\", \"output\": \"lethargic\"},\n",
    "    {\"input\": \"sunny\", \"output\": \"gloomy\"},\n",
    "    {\"input\": \"windy\", \"output\": \"calm\"},\n",
    "]\n",
    "\n",
    "example_prompt = PromptTemplate(\n",
    "    input_variables=[\"input\", \"output\"],\n",
    "    template=\"Input: {input}\\nOutput: {output}\",\n",
    ")\n",
    "example_selector = LengthBasedExampleSelector(\n",
    "    examples=examples, \n",
    "    example_prompt=example_prompt, \n",
    "    # 设定期望的示例文本长度\n",
    "    max_length=25\n",
    ")\n",
    "dynamic_prompt = FewShotPromptTemplate(\n",
    "    example_selector=example_selector,\n",
    "    example_prompt=example_prompt,\n",
    "    # 设置示例以外部分的前置文本\n",
    "    prefix=\"Give the antonym of every input\",\n",
    "    # 设置示例以外部分的后置文本\n",
    "    suffix=\"Input: {adjective}\\nOutput:\\n\\n\",\n",
    "    input_variables=[\"adjective\"],\n",
    ")\n",
    "\n",
    "# 当用户输入的内容比较少时，所有示例都足够被使用\n",
    "print(dynamic_prompt.format(adjective=\"big\"))\n",
    "\n",
    "# 当用户输入的内容足够长时，只有少量示例会被引用\n",
    "long_string = \"big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else\"\n",
    "print(dynamic_prompt.format(adjective=long_string))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "text='请回答下面的问题：\\n随机生成一位知名的作家及其代表作品\\n\\nThe output should be formatted as a JSON instance that conforms to the JSON schema below.\\n\\nAs an example, for the schema {\"properties\": {\"foo\": {\"title\": \"Foo\", \"description\": \"a list of strings\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"foo\"]}\\nthe object {\"foo\": [\"bar\", \"baz\"]} is a well-formatted instance of the schema. The object {\"properties\": {\"foo\": [\"bar\", \"baz\"]}} is not well-formatted.\\n\\nHere is the output schema:\\n```\\n{\"properties\": {\"name\": {\"title\": \"Name\", \"description\": \"name of an author\", \"type\": \"string\"}, \"book_names\": {\"title\": \"Book Names\", \"description\": \"list of names of book they wrote\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"name\", \"book_names\"]}\\n```\\n如果输出是代码块，请不要包含首尾的```符号'\n",
      "{\n",
      "    \"name\": \"J.K. Rowling\",\n",
      "    \"book_names\": [\n",
      "        \"Harry Potter and the Philosopher's Stone\", \n",
      "        \"Harry Potter and the Chamber of Secrets\", \n",
      "        \"Harry Potter and the Prisoner of Azkaban\", \n",
      "        \"Harry Potter and the Goblet of Fire\", \n",
      "        \"Harry Potter and the Order of the Phoenix\", \n",
      "        \"Harry Potter and the Half-Blood Prince\", \n",
      "        \"Harry Potter and the Deathly Hallows\"\n",
      "    ]\n",
      "}\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Actor(name='J.K. Rowling', book_names=[\"Harry Potter and the Philosopher's Stone\", 'Harry Potter and the Chamber of Secrets', 'Harry Potter and the Prisoner of Azkaban', 'Harry Potter and the Goblet of Fire', 'Harry Potter and the Order of the Phoenix', 'Harry Potter and the Half-Blood Prince', 'Harry Potter and the Deathly Hallows'])"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing import List\n",
    "\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_community.llms.ollama import Ollama\n",
    "from langchain.output_parsers import PydanticOutputParser\n",
    "from langchain.pydantic_v1 import BaseModel, Field\n",
    "\n",
    "class Actor(BaseModel):\n",
    "    name: str = Field(description=\"name of an author\")\n",
    "    book_names: List[str] = Field(description=\"list of names of book they wrote\")\n",
    "\n",
    "\n",
    "actor_query = \"随机生成一位知名的作家及其代表作品\"\n",
    "\n",
    "parser = PydanticOutputParser(pydantic_object=Actor)\n",
    "\n",
    "prompt = PromptTemplate(\n",
    "    template=\"请回答下面的问题：\\n{query}\\n\\n{format_instructions}\\n如果输出是代码块，请不要包含首尾的```符号\",\n",
    "    input_variables=[\"query\"],\n",
    "    partial_variables={\"format_instructions\": parser.get_format_instructions()},\n",
    ")\n",
    "\n",
    "input = prompt.format_prompt(query=actor_query)\n",
    "print(input)\n",
    "\n",
    "model = Ollama(model=\"llama2-chinese:13b\")\n",
    "output = model(input.to_string())\n",
    "\n",
    "print(output)\n",
    "parser.parse(output)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
